﻿using LearningWorkbench.Configuration;
using MongoDB.Bson;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Utility;

namespace LearningWorkbench
{
    public partial class CollectionPicker : Form
    {
        public InputsTargets InputsTargets { get; set; }
        public InputsTargets PreprocessedInputsTargets { get; set; }
        private Thread _processThread;

        public CollectionPicker()
        {
            InitializeComponent();
        }

        private void CollectionPicker_Load(object sender, EventArgs e)
        {
            var database = "test";

            DbManager.ConnectToDatabase(database);
            Console.WriteLine("Connected to " + database);

            var list = DbManager.ListCollections();

            comboBox1.Items.Clear();
            comboBox1.Items.AddRange(list.ToArray());

            _processThread = new Thread(() => { process(); });
        }

        private void savePreprocessedState()
        {
            InputsTargets newInputsTargets = new InputsTargets()
            {
                inputs = WorkbenchModel.PrepInputFieldNames,
                targets = WorkbenchModel.PrepTargetFieldNames
            };
            List<InputsTargets> dummyList = new List<Configuration.InputsTargets>();
            
            dummyList.Add(newInputsTargets);

            var itp = "input_targets_preprocessed.json";

            FileManager<InputsTargets>.Save(itp, dummyList);
            Console.WriteLine(itp + " has been created");

            var data = "preprocessed.json";

            List<string> fieldNames = new List<string>();

            fieldNames.AddRange(WorkbenchModel.PrepInputFieldNames);
            fieldNames.AddRange(WorkbenchModel.PrepTargetFieldNames);

            List<List<string>> values = new List<List<string>>();

            var inputs = WorkbenchModel.Inputs[0].Count();
            var targets = WorkbenchModel.Targets[0].Count();

            for (int i = 0; i < WorkbenchModel.Inputs.Count; ++i)
            {
                List<string> value = new List<string>();

                for (int j = 0; j < inputs; ++j)
                {
                    value.Add(WorkbenchModel.Inputs[i][j].ToString());
                }

                for (int j = 0; j < targets; ++j)
                {
                    value.Add(WorkbenchModel.Targets[i][j].ToString());
                }

                values.Add(value);
            }

            FileManager<string>.SaveAsCustomJSON(data, fieldNames, values);
        }

        private void preprocessData()
        {
            var collectionName = comboBox1.SelectedItem as string;
            WorkbenchModel.CurrentCollection = DbManager.GetCollection<LazyBsonDocument>(collectionName);

            WorkbenchModel.InputFieldNames = InputsTargets.inputs;
            WorkbenchModel.TargetFieldNames = InputsTargets.targets;
            WorkbenchModel.PrepInputFieldNames.AddRange(InputsTargets.inputs);
            WorkbenchModel.PrepTargetFieldNames.AddRange(InputsTargets.targets);

            foreach (var discrete in WorkbenchModel.ColumnDiscreteFields)
            {
                if (InputsTargets.inputs.Contains(discrete.column))
                {
                    WorkbenchModel.PrepInputFieldNames.Remove(discrete.column);

                    foreach (var field in discrete.fields)
                    {
                        WorkbenchModel.PrepInputFieldNames.Add(field.field);
                    }
                }
                else
                {
                    WorkbenchModel.PrepTargetFieldNames.Remove(discrete.column);

                    foreach (var field in discrete.fields)
                    {
                        WorkbenchModel.PrepTargetFieldNames.Add(field.field);
                    }
                }
            }

            Console.WriteLine("Discrete fields created!");

            foreach (var document in WorkbenchModel.CurrentCollection.FindAll())
            {
                var rawInput = InputTargetHandler.HandleInputs(document);
                var rawTarget = InputTargetHandler.HandleTargets(document);

                if (rawInput != null && rawTarget != null)
                {
                    WorkbenchModel.Ids.Add(document["_id"].AsObjectId.ToString());
                    WorkbenchModel.Inputs.Add(rawInput);
                    WorkbenchModel.Targets.Add(rawTarget);
                }
            }

            Console.WriteLine(WorkbenchModel.Inputs.Count + " input-target pair is added!");
        }

        private double getProcessedValue(string field, LazyBsonDocument document)
        {
            if (field.Contains("."))
            {
                var typeHierarchy = field.Split('.');

                return double.Parse(document[typeHierarchy[0]].
                    AsBsonDocument[typeHierarchy[1]].AsString);
            }

            return double.Parse(document[field].AsString);
        }

        private double[] handleProcessedInputs(LazyBsonDocument document)
        {
            var inputSize = PreprocessedInputsTargets.inputs.Count;

            double[] inputs = new double[inputSize];

            for (int i = 0; i < inputSize; ++i)
            {
                inputs[i] = getProcessedValue(PreprocessedInputsTargets.inputs[i], document);
            }

            return inputs;
        }

        private double[] handleProcessedTargets(LazyBsonDocument document)
        {
            var targetSize = PreprocessedInputsTargets.targets.Count;

            double[] targets = new double[targetSize];

            for (int i = 0; i < targetSize; ++i)
            {
                targets[i] = getProcessedValue(PreprocessedInputsTargets.targets[i], document);
            }

            return targets;
        }

        private void readProcessedData()
        {
            var collectionName = comboBox1.SelectedItem as string;
            WorkbenchModel.CurrentCollection = DbManager.GetCollection<LazyBsonDocument>(collectionName);

            WorkbenchModel.InputFieldNames = InputsTargets.inputs;
            WorkbenchModel.TargetFieldNames = InputsTargets.targets;
            WorkbenchModel.PrepInputFieldNames = PreprocessedInputsTargets.inputs;
            WorkbenchModel.PrepTargetFieldNames = PreprocessedInputsTargets.targets;

            foreach (var document in WorkbenchModel.CurrentCollection.FindAll())
            {
                var rawInput = handleProcessedInputs(document);
                var rawTarget = handleProcessedTargets(document);

                if (rawInput != null && rawTarget != null)
                {
                    WorkbenchModel.Ids.Add(document["_id"].AsObjectId.ToString());
                    WorkbenchModel.Inputs.Add(rawInput);
                    WorkbenchModel.Targets.Add(rawTarget);
                }
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (InputsTargets != null && WorkbenchModel.ColumnFilters != null &&
                WorkbenchModel.ColumnDiscreteFields != null &&
                WorkbenchModel.ColumnTransformations != null && WorkbenchModel.Normalizations != null &&
                comboBox1.SelectedItem != null)
            {
                _processThread.Start();
                DialogResult = System.Windows.Forms.DialogResult.OK;
            }
        }

        private void process()
        {
            if (!checkBox1.Checked)
            {
                Console.WriteLine("Data being preprocessed");
                preprocessData();
                savePreprocessedState();
                Console.WriteLine("Data preprocessing complete!");
            }
            else
            {
                Console.WriteLine("Using preprocessed data");
                readProcessedData();
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Transformation";

            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                WorkbenchModel.ColumnTransformations = 
                    FileManager<ColumnTransformation>.Load(openFileDialog1.FileName);
                Console.WriteLine(WorkbenchModel.ColumnTransformations.Count + 
                    " transformation rules added");
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Normalization";

            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                WorkbenchModel.Normalizations = 
                    FileManager<Normalization>.Load(openFileDialog1.FileName);
                Console.WriteLine(WorkbenchModel.Normalizations.Count + " normalization rules added");
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Filter";

            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                WorkbenchModel.ColumnFilters = 
                    FileManager<ColumnFilter>.Load(openFileDialog1.FileName);
                Console.WriteLine(WorkbenchModel.ColumnFilters.Count + " filtering rules added");
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Definition";

            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                InputsTargets = FileManager<InputsTargets>.Load(openFileDialog1.FileName)[0];
                Console.WriteLine(InputsTargets.inputs.Count + " inputs and " + 
                    InputsTargets.targets.Count + " targets added");
            }
        }

        private void button6_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Preprocessed Definition";

            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                PreprocessedInputsTargets = FileManager<InputsTargets>.Load(openFileDialog1.FileName)[0];
                Console.WriteLine(InputsTargets.inputs.Count + " inputs and " +
                    InputsTargets.targets.Count + " targets added");
            }
        }

        private void button7_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Discrete to Continous";

            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                WorkbenchModel.ColumnDiscreteFields =
                    FileManager<ColumnDiscreteField>.Load(openFileDialog1.FileName);
                Console.WriteLine(WorkbenchModel.ColumnDiscreteFields.Count +
                    " discrete to continous rules added");
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            label6.Visible = button6.Visible = checkBox1.Checked;
        }

        private void button8_Click(object sender, EventArgs e)
        {
            if (comboBox1.SelectedItem != null)
            {
                if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    var defaults = FileManager<Config>.Load(openFileDialog1.FileName)[0];
                    var path = openFileDialog1.FileName.Replace(openFileDialog1.SafeFileName, string.Empty);

                    WorkbenchModel.ColumnTransformations = 
                        FileManager<ColumnTransformation>.Load(path + defaults.transformation);
                    InputsTargets = FileManager<InputsTargets>.Load(path + defaults.definition)[0];
                    WorkbenchModel.Normalizations = 
                        FileManager<Normalization>.Load(path + defaults.normalization);
                    WorkbenchModel.ColumnFilters = 
                        FileManager<ColumnFilter>.Load(path + defaults.filter);
                    WorkbenchModel.ColumnDiscreteFields = 
                        FileManager<ColumnDiscreteField>.Load(path + defaults.discrete);

                    if (checkBox1.Checked)
                    {
                        PreprocessedInputsTargets =
                            FileManager<InputsTargets>.Load(path + defaults.predefinition)[0];
                    }

                    process();

                    DialogResult = System.Windows.Forms.DialogResult.OK;
                }
            }
        }
    }
}
